#!/usr/bin/env tsx

import { readFileSync } from 'fs'
import { glob } from 'glob'
import path from 'path'
import { fileURLToPath } from 'url'

const __scriptname = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__scriptname)

const PURPOSE_STRING = '@purpose Writer tool'
const DESCRIPTION_STRING = '@description'
const DESCRIPTION_REGEX = new RegExp(`${DESCRIPTION_STRING}\\s+(.+)`)

interface WriterTool {
  name: string
  description: string
  priority?: number // Lower numbers = higher priority
}

interface WriterToolsCollection {
  [category: string]: WriterTool[]
}

interface ScriptMetadata {
  isWriterTool?: boolean
  category?: string
  description?: string
}

// Manual entries for scripts that aren't TypeScript files with metadata
const MANUAL_ENTRIES: WriterToolsCollection = {
  'Validation and formatting': [
    { name: 'prettier', description: 'Format markdown, YAML, and other files' },
  ],
  Development: [
    { name: 'dev', description: 'Start local development server' },
    { name: 'build', description: 'Build the application' },
  ],
}

async function discoverWriterTools(): Promise<WriterToolsCollection> {
  const packageJsonPath = path.join(__dirname, '..', '..', 'package.json')
  const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
  const tools: WriterToolsCollection = { ...MANUAL_ENTRIES } // Start with manual entries

  // First get all files
  const allFiles = await glob('src/**/*', {
    cwd: path.join(__dirname, '..', '..'),
    absolute: true,
    ignore: ['**/node_modules/**', '**/tests/**', '**/test/**', '**/.*'],
  })

  // Then filter for .ts, .js, .sh scripts
  const scriptFiles = allFiles.filter((file) => {
    if (file === __scriptname) return false // skip the current file

    const ext = path.extname(file)
    if (['.ts', '.js', '.sh'].includes(ext)) return true

    // For extensionless files, check if they're executable or have shebang
    if (ext === '') {
      try {
        const content = readFileSync(file, 'utf8')
        return content.startsWith('#!/bin/bash') || content.startsWith('#!/usr/bin/env bash')
      } catch {
        return false
      }
    }
    return false
  })

  for (const filePath of scriptFiles) {
    try {
      const relativePath = path.relative(process.cwd(), filePath)
      const content = readFileSync(filePath, 'utf8')
      const metadata = extractMetadata(content)

      if (metadata.isWriterTool) {
        metadata.category = getCategory(relativePath)
        // Find corresponding npm script
        const scriptName = findScriptName(packageJson.scripts, relativePath)
        if (scriptName) {
          if (!tools[metadata.category]) tools[metadata.category] = []

          // Check if not already added manually
          const exists = tools[metadata.category].some((tool) => tool.name === scriptName)
          if (!exists) {
            tools[metadata.category].push({
              name: scriptName,
              description: metadata.description || `${scriptName} tool`,
            })
          }
        }
      }
    } catch {
      // Skip files that can't be read
      continue
    }
  }

  return tools
}

function extractMetadata(content: string): ScriptMetadata {
  const metadata: ScriptMetadata = {}
  const lines = content.split('\n').slice(0, 20) // Only check first 20 lines

  for (const line of lines) {
    if (line.includes(PURPOSE_STRING)) {
      metadata.isWriterTool = true
    }

    if (line.includes(DESCRIPTION_STRING)) {
      // Extract description from line like "@description Add content type frontmatter to articles"
      const match = line.match(DESCRIPTION_REGEX)
      if (match) {
        metadata.description = match[1].trim()
      }
    }
  }

  return metadata
}

// Convert the DIR in src/DIR/ to a title-cased category name
// E.g. src/secret-scanning becomes Secret Scanning
function getCategory(relativePath: string): string {
  const directory = relativePath.split(path.sep)[1]
  const category = directory
    .split('-')
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ')

  // Clarify this one category
  return category.replace('Content Render', 'Content Tasks')
}

function findScriptName(scripts: Record<string, string>, relativePath: string): string | null {
  for (const [scriptName, command] of Object.entries(scripts)) {
    // Check if the command includes this file path
    if (command.includes(relativePath)) {
      return scriptName
    }
    // Also check for simplified paths without the src/ prefix
    const simplifiedPath = relativePath.replace(/^src\//, '')
    if (command.includes(simplifiedPath)) {
      return scriptName
    }
  }
  return null
}

function prioritizeOrder(tools: WriterToolsCollection) {
  // Define priorities for specific tools
  const priorities = {
    'move-content': 1,
    'cta-builder': 2,
    'lint-content': 1,
    docstat: 1,
    dev: 1,
  }

  // Assign priorities to discovered tools
  for (const tool of Object.values(tools).flat()) {
    if (priorities[tool.name as keyof typeof priorities]) {
      tool.priority = priorities[tool.name as keyof typeof priorities]
    }
  }

  // Sort each category by priority, then alphabetically
  for (const category of Object.keys(tools)) {
    tools[category].sort((a, b) => {
      // Items with priority come first
      if (a.priority !== undefined && b.priority === undefined) return -1
      if (a.priority === undefined && b.priority !== undefined) return 1

      // Both have priority: sort by priority value
      if (a.priority !== undefined && b.priority !== undefined) {
        return a.priority - b.priority
      }

      // Neither has priority: sort alphabetically
      return a.name.localeCompare(b.name)
    })
  }

  return tools
}

async function main(): Promise<void> {
  console.log('For more info, run a command with "-- --help".\n')

  const tools = prioritizeOrder(await discoverWriterTools())

  for (const [category, scripts] of Object.entries(tools)) {
    console.log(`${category}:`)
    for (const script of scripts) {
      const padding = ' '.repeat(Math.max(0, 34 - script.name.length))
      console.log(`  npm run ${script.name}${padding}# ${script.description}`)
    }
    console.log('')
  }
}

if (import.meta.url === `file://${process.argv[1]}`) {
  try {
    await main()
  } catch (error) {
    console.error(error)
    process.exit(1)
  }
}
